# 所有的类默认继承object
class Teacher:  # 类,类对象

    # 类属性(类变量)
    company = '深蓝科技'
    a = 123

    # 方法: 在类中定义的函数叫方法
    # __init__ 称之为魔术方法(特殊方法): 官方定义好的,以两个下划线开头并且以两个下划线结尾的方法
    # 特点: 魔术方法一般不需要主动调用, 在特定情况下会自动执行
    def __init__(self, name, age, t_id):
        # 实例属性(实例变量)
        self.name = name
        self.age = age
        self.t_id = t_id
        self.a = 789

    # def teach(self, course):  # 对象方法,第一个参数位隐式的接受了实例对象
    #     return f'{self.name}在上{course}课'

    # 类方法
    # @classmethod  # 内置的类方法装饰器
    # def teach(cls, course):  # 类方法,第一个参数位置隐式的接收了类对象
    #     return f'{cls.company}的老师在上{course}课'

    # 静态方法
    @staticmethod  # 内置的静态方法装饰器
    def teach(course):
        return f'{Teacher.company}的老师在上{course}课'


"""
__new__: 实例化自动触发,构造方法, 用来创建并返回实例对象
__init__: 实例化自动出发,初始化方法, 用来定制属性, 不能有返回值(return None)

cls == class
每当执型实例化操作时,都会自动调用__new__(cls, *args, **kwargs)魔术方法
把要实例化的类对象(即: Teacher)作为实参传递给形参cls
实例化传递的其他实参(即 '张老师', 30, 111)传给不定长参数 *args, **kwargs
然后__new__会根据cls构造出一个实例对象并返回,返回给调用方(即: t1 = 实例对象)
接着还会自动调用__init__(self, name, age, t_id),把__new__构建的实例对象(即:t1)作为实参传递给形参self
实例化传递的其他实参(即 '张老师', 30, 111)传给形参name, age, t_id
然后__init__就会对实例对象(即: self)进行属性初始化
"""
t1 = Teacher('张老师', 30, 111)  # 实例化,返回实例对象
t2 = Teacher('李老师', 35, 112)
t3 = Teacher('王老师', 28, 113)

# t1.__init__('老余', t1.age, 114)
# print(t1.name, t1.age, t1.t_id)

"""
实例属性的增删改查
"""

""" 查
调用实例属性(是实例对象特有的): 只能用实例对象调用, 不能用类对象调用 
getattr(obj,name,[default])  返回obj(类,实例对象都可以使用)的name属性(name参数为字符串)
如果name属性不存在,返回default
判定属性是否存在 hasattr(obj,name) 返回bool类型 通过getattr()看是否有Error异常来判定
"""
# print(t1.t_id)
# print(t3.age)

# print(getattr(t1, 'address', '我也不知道他住哪里'))
# print(hasattr(Teacher, 'address'))
"""
调用类属性: 既可以被类对象调用(推荐), 也可以被实例对象调用 
如果类对象(Teacher)中有a属性, 实例对象(t1)中也有a属性, 会优先调用实例对象中的类属性
"""
# print(Teacher.company)
# print(t1.company)
# print(t1.a)  # 789 调用实例对象的a
# print(Teacher.a)  # 123 调用类对象的a

""" 
修改实例属性: 只能通过实例对象修改
setattr(obj,name,[value]) 可改可增  将obj的name(字符串)属性设置为value,属性不存在新增name属性
"""
t1.name = '老余'
t2.age = 40
# print(t1.name, t2.age)
# setattr(t1, 'name', '老尚')
# print(t1.name)

# 增
# setattr(t1, 'game', 'LOL')
# print(t1.game)
"""
修改类属性: 只能通过类对象修改
"""
# Teacher.company = '深蓝教育'
# print(Teacher.company)
# print(t1.company)


# 如果使用实例对象修改类属性
"""
动态定义实例属性 
不等于修改该实例对象调用的类属性, 实例对象只能访问到类属性, 不能去修改类属性
相当于给实例对象在__init__中添加了一个company属性
 """
t1.company = '深蓝教育'  # 给自己添加名为company的属性
# print(t1.company)  # 调用自己名为company的属性
# print(t2.company)  # t2没有company属性, 它调用的是Teacher类的属

""" 动态定义类属性 """
Teacher.company_address = '威宁路369号'
# print(Teacher.company_address)


"""
删除属性
函数: delattr(obj,name)  删除object(可以是类对象或实例对象) 的name属性(name参数为字符串)
"""
# del t1.age, Teacher.company

# print(t1.age)  # Error
# print(t2.company)  # 访问不到该属性, 因为Teacher类已经没有这个属性了, 它的实例化对象自然就访问不到这个属性了

# 使用函数删除属性
# delattr(t2, 'age')
# delattr(Teacher, 'company_address')  # 删除类属性
# print(t2.age)
# print()

""" 调用对象方法: 既可以使用实例对象调用(推荐),也可以使用类对象调用 """
# print(t1.teach('Python'))
# print(t3.teach('Java'))
# print(Teacher.teach(t1, 'Python'))  # 使用类对象调用,需要自己传入self

""" 调用类方法: 既可以用类对象调用(推荐), 也可以用实例对象调用 """
# print(Teacher.teach('Python'))
# print(t1.teach('Java'))

""" 调用静态方法: 既可以用类对象调用(推荐), 也可以用实例对象调用 """
print(Teacher.teach('Python'))


class Student:
    pass